package com.reyco1.platformengine.core
{
	import com.reyco1.platformengine.character.BaseCharacter;
	import com.reyco1.platformengine.character.Enemy;
	import com.reyco1.platformengine.map.IMap;
	import com.reyco1.platformengine.map.LevelMap;
	import com.reyco1.platformengine.tile.BaseTile;
	import com.reyco1.platformengine.tile.DoorTile;
	import com.reyco1.platformengine.tile.MovingTile;
	import com.reyco1.platformengine.vo.Coordinates;
	
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;

	public class GameEngine extends MovieClip
	{
		private var keyRight:Boolean;
		private var keyLeft:Boolean;
		private var keyUp:Boolean;
		private var keyDown:Boolean;
		private var keySpace:Boolean;
		
		private var mainCharacter:BaseCharacter;
		private var maps:Array 		 	= [];
		private var enemies:Array 		= [];
		private var movingTiles:Array 	= [];
		private var currentMap:IMap;
		
		private var visibleRows:Number;
		private var visibleColumns:Number;
		private var centerX:Number;
		private var centerY:Number;	
		private var shiftAmountX:Number;
		private var shiftAmountY:Number;
		private var charCenterXFactor:Number;
		private var charCenterYFactor:Number;
		
		public function GameEngine(columns:Number, rows:Number)
		{
			super();
			
			visibleColumns 	= columns;
			visibleRows 	= rows;
			
			addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
		}
		
		public function addMap(map:IMap, mapID:int, build:Boolean = false):void
		{
			maps.push( {map:map, id:mapID} );
			
			map.mapIndex = mapID;
			map.build();

			if(build)
			{
				currentMap = map as IMap;
				buildMap();
			}
		}	
		
		public function addCharacter(character:BaseCharacter, xTile:Number = 1, yTile:Number = 1, type:String = "hero", controllable:Boolean = false):void
		{
			character.controllable = controllable;
			character.engine 	   = this;
			character.map 		   = currentMap;
			
			if(type == "enemy")
			{
				enemies.push(character as Enemy);
			}
			
			if(controllable)
			{
				character.name = "mainCharacter";
				mainCharacter  = character;
			}
			
			placeCharacter(character, xTile, yTile);			
			addChild(character);			
		}
		
		public function addMovingTile(tile:MovingTile, xTile:Number = 1, yTile:Number = 1):void
		{
			var movingTile:MovingTile = tile;
			movingTile.build();
			
			movingTile.minX = movingTile.minX + xTile;
			movingTile.maxX = movingTile.maxX + xTile;
			movingTile.minY = movingTile.minY + yTile;
			movingTile.maxY = movingTile.maxY + yTile;
			
			movingTiles.push(movingTile);
			
			mainCharacter.movingTiles = movingTiles;
			
			placeDynamicTile(movingTile, xTile, yTile);
			addChild(movingTile);
		}
		
		private function changeMap(doorTile:BaseTile):void
		{
			render = false;
			
			var tile:DoorTile = DoorTile(doorTile);
			
			for(var a:int = 0; a<currentMap.tiles.length; a++)
			{
				removeChild(currentMap.tiles[a]);
			}
			
			var currentMapID:Number = currentMap.mapIndex;
			var nextMapID:Number 	= (tile.doorType == "forward") ? currentMapID + 1 : currentMapID - 1;
			var newCharTileX:Number = 1;
			var newCharTileY:Number = tile.row;
			var newMap:LevelMap 	= maps[nextMapID].map as LevelMap;
			
			currentMap = newMap as IMap;			
			buildMap();
			
			removeChild(mainCharacter);
			placeCharacter(mainCharacter, newCharTileX, newCharTileY);
			mainCharacter.map = currentMap;
			addChild(mainCharacter);
			
			render = true;
		}
		
		private function buildMap():void
		{
			for(var a:int = 0; a<currentMap.tiles.length; a++)
			{
				addChild(currentMap.tiles[a]);
			}
		}
		
		private function placeDynamicTile(tile:*, xTile:Number = 1, yTile:Number = 1):void
		{
			tile.tileX 	= xTile;
			tile.tileY 	= yTile;
			tile.x 		= (tile.tileX * currentMap.tileW) + (currentMap.tileW * 0.5);
			tile.y 		= (tile.tileY * currentMap.tileH) + (currentMap.tileH * 0.5);
		}
		
		private function placeCharacter(character:BaseCharacter, xTile:Number = 1, yTile:Number = 1):void
		{
			character.currentCoordinates = new Coordinates(xTile, yTile);
			character.x = (character.currentCoordinates.xTile * currentMap.tileW) + (currentMap.tileW * 0.5);
			character.y = ((character.currentCoordinates.yTile + 1) * currentMap.tileH) - (currentMap.tileH * 0.5);
			
			if(character.name == "mainCharacter")
			{
				character.checkForSlopes(xTile, yTile);
			}

			character.fall();		
		}
		
		private function detectKeys(e:Event):void
		{
			this.setChildIndex(mainCharacter, this.numChildren - 1);
			
			updateMovingTiles();
			mainCharacter.update();
			
			if(keySpace && !mainCharacter.isJumping)
			{
				if(mainCharacter.isOnSlope)
				{
					mainCharacter.y -= mainCharacter.yIncrement;
					mainCharacter.currentCoordinates.yTile = Math.floor(mainCharacter.y/currentMap.tileH);
				}
				
				mainCharacter.isJumping = true;
				mainCharacter.jumpSpeed = mainCharacter.jumpStart;
				mainCharacter.isOnSlope = false
			}
			
			if(keyRight)
			{
				if(!mainCharacter.checkForMovingTile(1))
				{
					mainCharacter.currentMovingTile = null;
				}
				
				mainCharacter.cornerTiles = CoordinateUtil.getCorners(mainCharacter, currentMap, this, mainCharacter.x - mainCharacter.speed, mainCharacter.y);
				if(!mainCharacter.isClimbing || mainCharacter.cornerTiles.downleft && mainCharacter.cornerTiles.upleft && mainCharacter.cornerTiles.downright)
				{
					mainCharacter.move(1, 0, 0);
				}				
			}
			else if(keyLeft)
			{
				if(!mainCharacter.checkForMovingTile(1))
				{
					mainCharacter.currentMovingTile = null;
				}
				
				mainCharacter.cornerTiles = CoordinateUtil.getCorners(mainCharacter, currentMap, this, mainCharacter.x - mainCharacter.speed, mainCharacter.y);
				if(!mainCharacter.isClimbing || mainCharacter.cornerTiles.downleft && mainCharacter.cornerTiles.upleft && mainCharacter.cornerTiles.downright)
				{
					mainCharacter.move(-1, 0, 0);	
				}				
			}
			else if(keyUp)
			{
				if(!mainCharacter.isJumping && mainCharacter.checkForUpLadder())
				{
					mainCharacter.climb(-1);
				}
			}
			else if(keyDown)
			{
				if(!mainCharacter.isJumping && mainCharacter.checkForDownLadder())
				{
					mainCharacter.climb(1);
				}
			}
			
			if(mainCharacter.isJumping)
			{
				mainCharacter.jump();
			}
			
			updateEnemies();
			checkForDoor();
			mainCharacter.update();
			updateMapScroll();
		}
		
		private function updateMovingTiles():void
		{
			for(var a:Number = 0; a<movingTiles.length; a++)
			{
				var movingTile:MovingTile = movingTiles[a] as MovingTile;
				
				movingTile.cornerTiles = CoordinateUtil.getCorners(movingTile, currentMap, this, movingTile.x + movingTile.speed * movingTile.dirX, movingTile.y + movingTile.speed * movingTile.dirY);
				
				if(movingTile.minY > movingTile.cornerTiles.upY || movingTile.maxY < movingTile.cornerTiles.downY)
				{
					movingTile.dirY = -movingTile.dirY;
				}
				if(movingTile.minX > movingTile.cornerTiles.leftX || movingTile.maxX < movingTile.cornerTiles.rightX)
				{
					movingTile.dirX = -movingTile.dirX;
				} 
				
				movingTile.x 	 = movingTile.x + movingTile.speed * movingTile.dirX;
			    movingTile.y 	 = movingTile.y + movingTile.speed * movingTile.dirY;
			    movingTile.tileX = Math.floor(movingTile.x / currentMap.tileW);
			    movingTile.tileY = Math.floor(movingTile.y / currentMap.tileH); 
			}
		}
		
		private function updateEnemies():void
		{
			for(var a:Number = 0; a<enemies.length; a++)
			{
				var enemy:Enemy = enemies[a] as Enemy;
				enemy.cornerTiles = CoordinateUtil.getCorners(enemy, currentMap, this, enemy.x + enemy.speed * enemy.direction, enemy.y + enemy.speed + 1);
				
				if(!enemy.cornerTiles.downleft&& !enemy.cornerTiles.downright && enemy.cornerTiles.upleft )
				{
					enemy.move(enemy.direction, 0, 0);
				}
				else
				{
					enemy.direction *= -1;
				}
				
				if(enemy.isJumping)
				{
					enemy.jump();
				}
				
				enemy.update();
			}
		}
		
		private function updateMapScroll():void
		{
			var newX:Number = centerX - mainCharacter.x;
			var newY:Number = centerY - mainCharacter.y
			
			if(newX > 0)
			{
				newX = 0;
			}
			else if(newX < (stage.stageWidth - width))
			{
				newX = stage.stageWidth - width;
			}
			
			if(newY > 0)
			{
				newY = 0;
			}
			else if(newY < (stage.stageHeight - height))
			{
				newY = stage.stageHeight - height;
			}
			
			x += (newX - x)/7;
			y += (newY - y)/7;
		}
		
		private function checkForDoor():void
		{
			if(BaseTile(getChildByName("tile_" + mainCharacter.currentCoordinates.yTile + "_" + mainCharacter.currentCoordinates.xTile)).isDoor)
			{				
				changeMap(BaseTile(getChildByName("tile_" + mainCharacter.currentCoordinates.yTile + "_" + mainCharacter.currentCoordinates.xTile)));
			}
		}
		
		/* main engine functions */		
		
        public function set render(value:Boolean):void
        {
        	if(value)
			{
        		addEventListener(Event.ENTER_FRAME, detectKeys);
        	}
			else
			{
        		removeEventListener(Event.ENTER_FRAME, detectKeys);
        	}
        }
        
        /*
        handlers
        */
		
		private function keyDownHandler(event:KeyboardEvent):void
        {
            switch(event.keyCode)
            {
                case Keyboard.UP:
                    keyUp 	= true;
                    keyDown = false;
                    break;
    
                case Keyboard.DOWN:
                    keyDown = true;
                    keyUp 	= false;
                    break;
    
                case Keyboard.LEFT:
                    keyLeft  = true;
                    keyRight = false;
                    break;
    
                case Keyboard.RIGHT:
                    keyRight = true;
                    keyLeft  = false;
                    break;
                    
                case Keyboard.SPACE:
                    keySpace = true;
                    break;
            }
        }
        
        private function keyUpHandler(event:KeyboardEvent):void
        {
            switch(event.keyCode)
            {
                case Keyboard.UP:
                    keyUp = false;
                    break;
    
                case Keyboard.DOWN:
                    keyDown = false;
                    break;
    
                case Keyboard.LEFT:
                    keyLeft = false;
                    break;
    
                case Keyboard.RIGHT:
                    keyRight = false;
                    break;
                    
                case Keyboard.SPACE:
                    keySpace = false;
                    break;
            }
        }
        
        private function handleAddedToStage(e:Event):void
        {
        	stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
            stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);            
            centerY = stage.stageHeight * 0.5;
            centerX = stage.stageWidth  * 0.5;
        }
		
	}
}